-
Notifications
You must be signed in to change notification settings - Fork 242
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
agetpass(): Allocate on the stack (alloca(3)) #1191
base: master
Are you sure you want to change the base?
agetpass(): Allocate on the stack (alloca(3)) #1191
Conversation
cafa934
to
1154d32
Compare
f6aeb0a
to
b7c072b
Compare
4545946
to
30b50b1
Compare
This comment was marked as outdated.
This comment was marked as outdated.
30b50b1
to
f024191
Compare
I've rewritten the patches from scratch as v4. |
f024191
to
2f18365
Compare
57b8ac5
to
c7ea351
Compare
c7ea351
to
0a5c77e
Compare
fb69c8d
to
d6ceb22
Compare
This simplifies the agetpass() call into a single line. Signed-off-by: Alejandro Colomar <[email protected]>
The lengthy documentation is rather obvious, and only clutters the source file. It will still be reachable in the git history for those interested. Instead, just say that this function is basically getpass(3) done right. Signed-off-by: Alejandro Colomar <[email protected]>
9c49413
to
1d7a2ea
Compare
ec6acbf
to
2825292
Compare
readpassphrase(3) is hard to use correctly. Wrap correct usage of readpassphrase in this API. Signed-off-by: Alejandro Colomar <[email protected]>
Signed-off-by: Alejandro Colomar <[email protected]>
Signed-off-by: Alejandro Colomar <[email protected]>
Signed-off-by: Alejandro Colomar <[email protected]>
Signed-off-by: Alejandro Colomar <[email protected]>
This macro will allow using alloca(3) memory in these APIs. Signed-off-by: Alejandro Colomar <[email protected]>
Signed-off-by: Alejandro Colomar <[email protected]>
… APIs These APIs will minimize the visibility of passwords, by not using the heap. The stack should have enough space for PASS_MAX+2 allocations, so this should be safe. Signed-off-by: Alejandro Colomar <[email protected]>
And getpassa_stdin() instead of agetpass_stdin(). Now all passwords live in the stack, and are never copied into the heap. This introduces a subtle issue: while it's fine to call malloc(3) in a loop, it is dangerous to call alloca(3) in a loop (since there's no way to free that memory). The next commit will fix that. I've addressed it in a separate commit, for readability. Signed-off-by: Alejandro Colomar <[email protected]>
Calling passalloca() (which is a wrapper around alloca(3)) in a loop is dangerous, as it can trigger a stack overflow. Instead, allocate the buffer before the loop, and run getpass2() within the loop, which will reuse the buffer. Also, to avoid deallocator mismatches, use `pass = passzero(pass)` in those cases, so that the compiler knows that 'pass' has changed, and we're not using the password after zeroing it; we're only re-using its storage, which is fine. Signed-off-by: Alejandro Colomar <[email protected]>
In the last commit, we replaced all of these calls by alloca(3)-based variants. Signed-off-by: Alejandro Colomar <[email protected]>
2825292
to
2a5cf77
Compare
@jubalh Do you know why the opensuse CI is failing? It's something about the package manager, it seems. |
Thank you for your work on these patches. I've started reviewing them and I have a few questions to help me better understand the overall goal. I appreciate you've been working to improve the project and introduce new APIs for things like password handling. While I understand the initial need for these APIs, I'm finding the frequent modifications a bit challenging to follow. Can you provide some more context around the long-term vision for these APIs? Understanding the ultimate goal would help assess the individual changes and provide more meaningful feedback. |
The goal of the password APIs is that
Currently, we read the passwords with agetpass(), which provides the first guarantee and the third, but not the second. This has another problem: there might be truncation or buffer overflows if the buffers don't match in size. The solution to that is using lib/pass/ APIs when handling those copies too. |
Here's the list of APIs that I want to have: // There is also a limit in PAM (PAM_MAX_RESP_SIZE), currently set to 512.
#ifndef PASS_MAX
# define PASS_MAX (BUFSIZ - 1)
#endif
// Similar to getpass(3), but free of its problems, and get the buffer in $1.
#define getpass2(buf, prompt) readpass(buf, prompt, RPP_REQUIRE_TTY)
#define getpass2_stdin(buf) readpass(buf, NULL, RPP_STDIN)
// Similar to getpass(3), but free of its problems, and using alloca(3).
#define getpassa(prompt) getpass2(passalloca(), prompt)
#define getpassa_stdin() getpass2_stdin(passalloca())
#define passalloca() ALLOCA(1, pass_t)
#define passcalloca() passdupa(&(pass_t){""})
#define passdupa(pass) passcpy(passalloca(), pass)
typedef typeof(char [PASS_MAX + 2]) pass_t;
ATTR_MALLOC(passzero)
ATTR_STRING(2)
pass_t *passcpy(pass_t *restrict dst, const pass_t *restrict src);
ATTR_STRING(1)
pass_t *passzero(pass_t *pass);
ATTR_MALLOC(passzero)
ATTR_STRING(2)
pass_t *readpass(pass_t *restrict pass, const char *restrict prompt, int flags); of which, |
Hmmm, since most of them are one-liners, and only a couple of functions, maybe I can keep a single pair of .c/.h files. |
Hi!
This is rather sensitive, and I'd like to have as many eyes as possible look at this code.
Cc: @hallyn , @ikerexxe , @stoeckmann , @thalman , @thesamesam , @ferivoz , @jubalh
Reasons for all this change:
See Clear plaintext passwords in more error cases #1190 (comment).
Revisions:
v2
v2b
v3
v3b
v4
[[gnu::malloc()]]
, due to https://inbox.sourceware.org/gcc/dese7p5pdgne5gtumus6mc6ydlfcnwneeovsujpbvwqibe52ax@sl3uip7dwxg6/T/.v5
<pass/limits.h>
. This breaks a circular include.v6
v6b
v6c
v7
This fixes an accidental bug I had introduced earlier. In src/sulogin.c, I was passing a NULL to passzero().
Code is also much simpler (and safer) when you can pass NULL to destructor APIs.
v7b
v7c
v8
v8b
v9
v10
v10b
restrict
.v11
v12
v13
v13b
Comparison against v12, as a sanity check: